
/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#include <mem.h>
#include <cpu.h>
#include <arch/sysregs.h>

/****************************************************************************
 ****                           公共函数定义                             ****
 ****************************************************************************/

/**
 * 功能描述: 地址空间初始化（根据hypervisor空间，拷贝空间，VM空间添加到根页表）
 * 入口参数: as:    地址空间
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void as_arch_init(struct addr_space* as)
{
    size_t index;

    /*
     * 如果地址空间是已经存在的hypervisor空间的副本，是不可能使用PT_CPU_REC_IND
     * 索引去导航的。所以，我们必须使用 PT_VM_REC_IND索引。
     * 实际上，PT_CPU_REC_IND是虚拟核的第一级页表中最后一项；
     *         PT_VM_REC_IND是虚拟核的第一级页表中的倒数第二项；
     */
    if (as->type == AS_HYP_CPY || as->type == AS_VM) {
        index = PT_VM_REC_IND;      /* CPY和VM的地址空间 */
    } else {
        index = PT_CPU_REC_IND;     /* Hypervisor的地址空间 */
    }

    /* 将页表添加到根页表中 */
    pt_set_recursive(&as->pt, index);
}

/**
 * 功能描述: 填充内存
 *           （将虚拟地址转换为物理地址，然后将结果写入到指定的地址中）
 * 入口参数: as:    要转换的虚拟地址属于的地址空间
 *           va:    要转换的虚拟地址
 *           pa:    保存映射关系的地址，也就是页表的保存位置
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
bool mem_translate(struct addr_space* as, vaddr_t va, paddr_t* pa)
{
    uint64_t par = 0, par_saved = 0;

    /**
     * TODO: are barriers needed in this operation?
     */

    par_saved = MRS(PAR_EL1);

    /* 如果是hypervisor自身地址空间，或者为拷贝VM而使用的地址空间，
     * 则属于`EL2`的stage-1地址转换。如果是VM的地址空间，则属于
     * EL1的`stage-1&2`地址转换。
     */
    if (as->type == AS_HYP || as->type == AS_HYP_CPY)
        asm volatile("AT S1E2W, %0" ::"r"(va));
    else
        asm volatile("AT S12E1W, %0" ::"r"(va));

    par = MRS(PAR_EL1);
    MSR(PAR_EL1, par_saved);
    if (par & PAR_F) {
        return false;
    } else {
        if (pa != NULL)
            *pa = (par & PAR_PA_MSK) | (va & (PAGE_SIZE - 1));
        return true;
    }
}